/*  Copyright (C) 2008  Alexander Blyzniuchenko (BlezAlex)

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;

namespace DC.Http
{
    class Request
    {
        private const string PAIR_DELIMITER = ":";

        private readonly string _rawUrl;
        private readonly string _url;
        private readonly EndPoint _remoteEndPoint;
        private readonly Dictionary<string, string> _headers = new Dictionary<string, string>();
        private readonly RequestMethod _method;
        private readonly Stream _inputStream;

        public Request(string requestString, Stream inputStream, EndPoint remoteEndPoint)
        {
            using (StringReader sr = new StringReader(requestString))
            {
                string line = sr.ReadLine();
                if (string.IsNullOrEmpty(line))
                    throw new InvalidReqeustException("Request method is not specified. First line is empty");

                string[] firstLineValues = line.Split(' ');
                _method = (RequestMethod)Enum.Parse(typeof(RequestMethod), firstLineValues[0].Trim());
                _rawUrl = firstLineValues[1].Trim();
                _url = HttpUtility.DecodeURL(_rawUrl).Replace("//", "/");
                while (!string.IsNullOrEmpty(line = sr.ReadLine()))
                {
                    if (line.Length > line.IndexOf(PAIR_DELIMITER) + 2)
                    {
                        string key = line.Substring(0, line.IndexOf(PAIR_DELIMITER));
                        string value = line.Substring(line.IndexOf(PAIR_DELIMITER) + 1);

                        if (!_headers.ContainsKey(key))
                            _headers.Add(key, value.Trim());
                    }
                }
            }
            _remoteEndPoint = remoteEndPoint;
            _inputStream = inputStream;
        }

        public Stream InputStream
        {
            get { return _inputStream; }
        }

        public string RawUrl
        {
            get { return _rawUrl; }
        }

        public string Url
        {
            get { return _url; }
        }

        public EndPoint RemoteEndPoint
        {
            get { return _remoteEndPoint; }
        }

        public Dictionary<string, string> Headers
        {
            get { return _headers; }
        }

        public RequestMethod Method
        {
            get { return _method; }
        }


    }

    class Response : Stream 
    {
        private bool _closed = false;
        private bool _isHeaderSent = false;
        private readonly object _synObject = new object();
        private readonly NetworkStream _stream;
        private static readonly Encoding _win1251Encoding = Encoding.GetEncoding(1251);

        public TextWriter Output = new StringWriter();
        public long ContentLength64 = -1;
        public int StatusCode = 200; 
        public string StatusDescription = "OK";
        public Dictionary<string, string> Headers = new Dictionary<string, string>();

        public Response(NetworkStream ns)
        {
            _stream = ns; 
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            if (!_isHeaderSent)
                WriteHttpInfo();

            _isHeaderSent = true;

            _stream.Write(buffer, offset, count);
        }

        public override void Close()
        {
            try
            {
                lock (_synObject)
                {
                    if (_closed)
                        return;

                    if (!_isHeaderSent)
                        WriteHttpInfo();

                    _closed = true;
                }
                _stream.Close();
            }
            catch(Exception ex)
            {
                Debug.Write(ex);
            }
            
        }

        private void WriteHttpInfo()
        {
            if (ContentLength64 >= 0)
                Headers.Add("Content-Length", ContentLength64.ToString());

            StringBuilder sb = new StringBuilder();
            sb.AppendFormat("HTTP/1.1 {0} {1}\r\n", StatusCode, StatusDescription);
            foreach (KeyValuePair<string, string> pair in Headers)
            {
                sb.AppendFormat("{0}: {1}\r\n", pair.Key, pair.Value);
            }
            sb.AppendLine();
            byte[] result = _win1251Encoding.GetBytes(sb.ToString());
            _stream.Write(result, 0, result.Length);
        }

        #region "Delegation"
        public override void Flush()
        {
            _stream.Flush();
        }

        public override bool CanTimeout
        {
            get { return _stream.CanTimeout; }
        }

        public override bool CanWrite
        {
            get { return _stream.CanWrite; }
        }
        #endregion


        public override bool CanRead
        {
            get { return false; }
        }

        public override bool CanSeek
        {
            get { return false; }
        }

        #region "Stubs"

        public override long Seek(long offset, SeekOrigin origin)
        {
            throw new NotSupportedException();
        }

        public override void  SetLength(long value)
        {
            throw new NotSupportedException();
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            throw new NotSupportedException();
        }

        public override long Length
        {
            get { throw new NotSupportedException(); }
        }

        public override long Position
        {
            get { throw new NotSupportedException(); }
            set { throw new NotSupportedException(); }
        }

        public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
        {
            throw new NotSupportedException();
        }

        public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
        {
            throw new Exception("The method or operation is not implemented.");
        }

        #endregion

    }
}